package com.example.vehicle.Controller;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import cn.hutool.crypto.digest.HMac;
import cn.hutool.crypto.digest.HmacAlgorithm;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.example.vehicle.Entity.VehicleUser;
import com.example.vehicle.HelloApplication;
import com.example.vehicle.Utils.HttpClientUtil;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.TextField;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * @name: HelloViewController
 * @description: 注册登录界面控制器类。
 * @author: Lulu Zhao
 **/
public class HelloViewController {
    @FXML
    public TextField idText;

    @FXML
    public TextField pwText;

    @FXML
    public TextField idTextField;

    @FXML
    public TextField pwTextField;

    private int registerButtonClickTime = 0;

    VehicleUser vehicleUser = new VehicleUser();

    /**
     * @name: onRegisterButtonClick
     * @description: 注册按钮回调函数。
     * @params: []
     * @return: void
     * @author: Lulu Zhao
     */
    @FXML
    protected void onRegisterButtonClick() {
        if (++registerButtonClickTime != 1) {
            Alert alert = new Alert(Alert.AlertType.WARNING, "请勿频繁注册用户！");
            alert.showAndWait();
            return;
        }
        // 模拟用户递交身份信息给TA，TA分配ID和初始口令的过程。
        vehicleUser = vehicleUserRegister();
        String clientID = vehicleUser.getClientID();
        String initPassword = vehicleUser.getInitPassword();
        idTextField.setText("id: " + clientID);
        pwTextField.setText("init_pw: " + initPassword);
        // 与RSU交互完成用户注册。
        vehicleUserRegisterWithNetwork(vehicleUser);
    }

    /**
     * @name: vehicleUserRegisterWithNetwork
     * @description: 与RSU交互完成用户注册。
     * @params: [vehicleUser]
     * @return: void
     * @author: Lulu Zhao
     */
    private void vehicleUserRegisterWithNetwork(VehicleUser vehicleUser) {
        Map<String, String> param = new HashMap<>();
        param.put("vehicleId", vehicleUser.getClientID());
        param.put("hashedPassword", vehicleUser.getHashedPassword());
        param.put("currentHashTime", String.valueOf(vehicleUser.getCurrentHashTimes()));
        param.put("currentState", "init");
        param.put("location", vehicleUser.getLocation());
        HttpClientUtil.doPost("http://localhost:8080/vehcc/register", param);
    }

    /**
     * @name: vehicleUserRegister
     * @description: 模拟用户递交身份信息给TA，TA分配ID和初始口令的过程。
     * @params: []
     * @return: com.example.vehicle.Entity.VehicleUser
     * @author: Lulu Zhao
     */
    private VehicleUser vehicleUserRegister() {
        String clientID = IdUtil.simpleUUID();
        String initPassword = RandomUtil.randomString(20);
        int currentHashTimes = 30;
        String calPassword = initPassword;
        for (int i = 0; i < currentHashTimes; i++) {
            calPassword = SmUtil.sm3(calPassword);
        }
        String hashedPassword = calPassword;
        // Todo: Get the location of the vehicle.
        return new VehicleUser(clientID, initPassword, hashedPassword, currentHashTimes, "6,6");
    }

    /**
     * @name: onLoginButtonClick
     * @description: 登录按钮回调函数。
     * @params: []
     * @return: void
     * @author: Lulu Zhao
     */
    @FXML
    protected void onLoginButtonClick() throws IOException {
        String vehicleId = idText.getText();
        String previousHashedPassword = pwText.getText();
        // 通过车辆ID检查用户是否完成注册。
        if (!vehicleUserLoginWithNetwork(vehicleId)) {
            Alert alert = new Alert(Alert.AlertType.ERROR, "此用户不存在！");
            alert.showAndWait();
            return;
        }
        // 进行身份验证，获取RSU公钥证书。
        if (isVehicleUserValid(vehicleId, previousHashedPassword)) {
            // 验证成功则生成对称密钥和Hmac密钥。
            HelloApplication.loginUser.setClientID(vehicleId);
            SymmetricCrypto sm4 = new SymmetricCrypto("SM4/ECB/PKCS5Padding");
            HelloApplication.key.setKeySM4(sm4.getSecretKey().getEncoded());
            HelloApplication.key.setKeyHmac("hmackey".getBytes());
            // 密钥协商，成功则跳转至应用主界面。
            if (keyAgreement(vehicleId)) {
                pageSkip();
            }
        }
    }

    /**
     * @name: keyAgreement
     * @description: 密钥协商过程。
     * @params: [vehicleId]
     * @return: boolean
     * @author: Lulu Zhao
     */
    private boolean keyAgreement(String vehicleId) {
        // 生成消息，其格式为：
        // E_SM2(KeySM4 || KeyHmac) || "@" || E_Hmac(Base64(E_SM2(KeySM4 || KeyHmac)))
        byte[] message = new byte[HelloApplication.key.getKeySM4().length + HelloApplication.key.getKeyHmac().length];
        System.arraycopy(HelloApplication.key.getKeySM4(), 0, message, 0, HelloApplication.key.getKeySM4().length);
        System.arraycopy(HelloApplication.key.getKeyHmac(), 0, message, HelloApplication.key.getKeySM4().length, HelloApplication.key.getKeyHmac().length);
        SM2 sm2 = new SM2(null, HelloApplication.key.getPublicKey());
        String encryptMessage = Base64.encode(sm2.encrypt(message, KeyType.PublicKey));
        HMac mac = new HMac(HmacAlgorithm.HmacSM3, HelloApplication.key.getKeyHmac());
        String messageMac = mac.digestHex(encryptMessage);
        String finalMessage = encryptMessage + "@" + messageMac;
        System.out.println(finalMessage);
        // 与RSU交互完成密钥协商。
        Map<String, String> param = new HashMap<>();
        param.put("vehicleId", vehicleId);
        param.put("message", finalMessage);
        String response = HttpClientUtil.doPost("http://localhost:8080/vehcc/keyagreement", param);
        JSONObject responseJson = JSONUtil.parseObj(response);
        return responseJson.get("status").toString().equals("ok");
    }

    /**
     * @name: isVehicleUserValid
     * @description: 进行身份验证，获取RSU公钥证书。
     * @params: [vehicleId, previousHashedPassword]
     * @return: boolean
     * @author: Lulu Zhao
     */
    private boolean isVehicleUserValid(String vehicleId, String previousHashedPassword) {
        Map<String, String> param = new HashMap<>();
        param.put("vehicleId", vehicleId);
        param.put("previousHashedPassword", previousHashedPassword);
        String response = HttpClientUtil.doGet("http://localhost:8080/vehcc/validate", param);
        JSONObject responseJson = JSONUtil.parseObj(response);
        if (responseJson.get("status").toString().equals("needToBeUpdated")) {
            HelloApplication.loginUser.setCurrentHashTimes(Integer.parseInt(responseJson.get("payload").toString()));
            HelloApplication.key.setPublicKey(responseJson.get("certificate").toString());
            Alert alert = new Alert(Alert.AlertType.WARNING, "需要向TA申请新口令！");
            alert.showAndWait();
            return true;
        } else if (responseJson.get("status").toString().equals("failed")) {
            Alert alert = new Alert(Alert.AlertType.ERROR, "动态口令验证失败！");
            alert.showAndWait();
            return false;
        }
        HelloApplication.loginUser.setCurrentHashTimes(Integer.parseInt(responseJson.get("payload").toString()));
        HelloApplication.key.setPublicKey(responseJson.get("certificate").toString());
        return true;
    }

    /**
     * @name: vehicleUserLoginWithNetwork
     * @description: 通过车辆ID检查用户是否完成注册。
     * @params: [vehicleId]
     * @return: boolean
     * @author: Lulu Zhao
     */
    private boolean vehicleUserLoginWithNetwork(String vehicleId) {
        Map<String, String> param = new HashMap<>();
        param.put("vehicleId", vehicleId);
        String response = HttpClientUtil.doGet("http://localhost:8080/vehcc/queryVehicleById", param);
        JSONObject responseJson = JSONUtil.parseObj(response);
        return responseJson.get("status").toString().equals("ok");
    }

    /**
     * @name: pageSkip
     * @description: 跳转至应用主界面。
     * @params: []
     * @return: void
     * @author: Lulu Zhao
     */
    private void pageSkip() throws IOException {
        FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("main-view.fxml"));
        Scene scene = new Scene(fxmlLoader.load());
        HelloApplication.stage.setScene(scene);
    }

    /**
     * @name: debugMode
     * @description: debug模式用于直接进入主界面。
     * @params: []
     * @return: void
     * @author: Lulu Zhao
     */
    @FXML
    public void debugMode() throws IOException {
        pageSkip();
    }
}
